﻿#include "pch.h"
#include "Const.h"
#include "Snake.h"

/**
 * 构造方法。
 */
Snake::Snake(CDC* drawer):
    mDirection(VK_DOWN) {
    // 初始化成员
    CPoint caudaPos(0, 0);
    mHead = mCauda = new SnakeBody(drawer, &caudaPos, SnakeBody::SNAKE);
}

/**
 * 析构方法。
 */
Snake::~Snake() {
    // 从蛇尾开始，删除所有蛇身
    SnakeBody* snakeBody = mCauda->GetNext();
    while (snakeBody != NULL) {
        delete mCauda;
        mCauda = snakeBody;
        snakeBody = snakeBody->GetNext();
    }
    delete mHead;
}

/**
 * 吃果子，即将果子放到蛇尾。
 * @param apple 果子
 */
void Snake::Eat(SnakeBody* apple) {
    // 设置吃到的果子的位置
    CPoint position(*mHead->GetPosition());
    switch (mDirection) {
        case VK_DOWN:
            position.y++;
            break;
        case VK_LEFT:
            position.x--;
            break;
        case VK_RIGHT:
            position.x++;
            break;
        case VK_UP:
            position.y--;
            break;
    }
    apple->SetPosition(&position);

    // 改变果子的类型
    apple->SetType(SnakeBody::SNAKE);

    // 重绘蛇头和果子
    mHead->Draw();
    apple->Draw();

    // 将吃到的果子变成蛇头
    mHead->SetNext(apple);
    mHead = apple;
}

/**
 * 获取蛇头。
 * @return 蛇头
 */
SnakeBody* Snake::GetHead() {
    return mHead;
}

/**
 * 移动。
 * @return 蛇头碰到自身或游戏区域边界则返回 TRUE，否则返回 FALSE
 */
BOOL Snake::Move() {
    // 计算蛇尾的新位置并抹除蛇尾
    CPoint point(*mHead->GetPosition());
    switch (mDirection) {
        case VK_DOWN:
            if (point.y + 1 >= Const::GAME_BOARD_GRID_COUNT_V) {
                // 如果蛇头超出游戏区域下边界，则返回
                return TRUE;
            }
            mCauda->Erase();
            point.y++;
            break;
        case VK_LEFT:
            if (point.x - 1 < 0) {
                // 如果蛇头超出游戏区域左边界，则返回
                return TRUE;
            }
            mCauda->Erase();
            point.x--;
            break;
        case VK_RIGHT:
            if (point.x + 1 >= Const::GAME_BOARD_GRID_COUNT_H) {
                return TRUE;    // 如果蛇头超出游戏区域右边界，则返回
            }
            mCauda->Erase();
            point.x++;
            break;
        case VK_UP:
            if (point.y - 1 < 0) {
                return TRUE;    // 如果蛇头超出游戏区域上边界，则返回
            }
            mCauda->Erase();
            point.y--;
            break;
    }
    mCauda->SetPosition(&point);

    // 将蛇尾的下一个结点设置为蛇尾、原蛇尾设置成蛇头
    mHead->SetNext(mCauda);
    mHead = mCauda;
    mCauda = mCauda->GetNext();
    mHead->SetNext(NULL);
    mHead->Draw();

    // 检查蛇头是否碰到蛇身
    for (SnakeBody* snakeBody = mCauda; snakeBody != mHead; snakeBody = snakeBody->GetNext()) {
        if (snakeBody->GetPosition() == mHead->GetPosition()) {
            return TRUE;
        }
    }

    return FALSE;
}

/**
 * 检查某点是否与蛇身位置重叠。
 * @param point 被检查的点
 * @return 点与蛇身位置重叠则返回 TRUE ，否则返回 FALSE
 */
BOOL Snake::PointIsOnMe(CPoint* point) {
    for (SnakeBody* snakeBody = mCauda; snakeBody != NULL; snakeBody = snakeBody->GetNext()) {
        if (*snakeBody->GetPosition() == *point) {
            return TRUE;
        }
    }
    return FALSE;
}

/**
 * 转弯。
 * @param direction 方向
 */
void Snake::Turn(UINT direction) {
    // 如果方向与当前方向相反（想要直接调头），则直接返回
    if (abs((int)(direction - mDirection)) == 2) {
        return;
    }
    mDirection = direction;
}
